#include "stdafx.h"
#include "stream/binarywriter.hpp"
#include <algorithm>

namespace JHCPP
{
	namespace stream
	{
		CBinaryWriter::CBinaryWriter(std::ostream& ostr, StreamByteOrder byteOrder) : m_ostr(ostr), m_pTextConverter(0)
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			m_flipBytes = (byteOrder == LITTLE_ENDIAN_BYTE_ORDER);
		#else
			m_flipBytes = (byteOrder == BIG_ENDIAN_BYTE_ORDER);
		#endif
		}

		CBinaryWriter::CBinaryWriter(std::ostream& ostr, CTextEncode& encode, StreamByteOrder byteOrder):
		m_ostr(ostr),
			m_pTextConverter(new CTextConverter(CTextEncode::global(), encode))
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			m_flipBytes = (byteOrder == LITTLE_ENDIAN_BYTE_ORDER);
		#else
			m_flipBytes = (byteOrder == BIG_ENDIAN_BYTE_ORDER);
		#endif
		}

		CBinaryWriter::~CBinaryWriter()
		{
			delete m_pTextConverter;
		}

		CBinaryWriter& CBinaryWriter::operator << (bool value)
		{
			m_ostr.write((const char*) &value, sizeof(value));
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (char value)
		{
			m_ostr.write((const char*) &value, sizeof(value));
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (unsigned char value)
		{
			m_ostr.write((const char*) &value, sizeof(value));
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (signed char value)
		{
			m_ostr.write((const char*) &value, sizeof(value));
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (short value)
		{
			if (m_flipBytes)
			{
				short fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (unsigned short value)
		{
			if (m_flipBytes)
			{
				unsigned short fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (int value)
		{
			if (m_flipBytes)
			{
				int fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (unsigned int value)
		{
			if (m_flipBytes)
			{
				unsigned int fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (long value)
		{
			if (m_flipBytes)
			{
			#if defined(JH_LONG_IS_64_BIT)
				long fValue = CByteOrder::flipBytes((Int64) value);
			#else
				long fValue = CByteOrder::flipBytes((Int32) value);
			#endif
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (unsigned long value)
		{
			if (m_flipBytes)
			{
			#if defined(JH_LONG_IS_64_BIT)
				long fValue = CByteOrder::flipBytes((UInt64) value);
			#else
				long fValue = CByteOrder::flipBytes((UInt32) value);
			#endif
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (float value)
		{
			if (m_flipBytes)
			{
				const char* ptr = (const char*) &value;
				ptr += sizeof(value);
				for (unsigned i = 0; i < sizeof(value); ++i)
					m_ostr.write(--ptr, 1);
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (double value)
		{
			if (m_flipBytes)
			{
				const char* ptr = (const char*) &value;
				ptr += sizeof(value);
				for (unsigned i = 0; i < sizeof(value); ++i)
					m_ostr.write(--ptr, 1);
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (Int64 value)
		{
			if (m_flipBytes)
			{
				Int64 fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (UInt64 value)
		{
			if (m_flipBytes)
			{
				UInt64 fValue = CByteOrder::flipBytes(value);
				m_ostr.write((const char*) &fValue, sizeof(fValue));
			}
			else
			{
				m_ostr.write((const char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (const std::string& value)
		{
			if (m_pTextConverter)
			{
				std::string converted;
				m_pTextConverter->convert(value, converted);
				UInt32 length = (UInt32) converted.size();
				write7BitEncoded(length);
				m_ostr.write(converted.data(), length);
			}
			else
			{
				UInt32 length = (UInt32) value.size();
				write7BitEncoded(length);
				m_ostr.write(value.data(), length);
			}
			return *this;
		}

		CBinaryWriter& CBinaryWriter::operator << (const char* value)
		{
			jh_check_ptr (value);

			if (m_pTextConverter)
			{
				std::string converted;
				m_pTextConverter->convert(value, static_cast<int>(std::strlen(value)), converted);
				UInt32 length = (UInt32) converted.size();
				write7BitEncoded(length);
				m_ostr.write(converted.data(), length);
			}
			else
			{
				UInt32 length = static_cast<UInt32>(std::strlen(value));
				write7BitEncoded(length);
				m_ostr.write(value, length);
			}
			return *this;
		}

		void CBinaryWriter::write7BitEncoded(UInt32 value)
		{
			do
			{
				unsigned char c = (unsigned char) (value & 0x7F);
				value >>= 7;
				if (value) c |= 0x80;
				m_ostr.write((const char*) &c, 1);
			}
			while (value);
		}

		void CBinaryWriter::write7BitEncoded(UInt64 value)
		{
			do
			{
				unsigned char c = (unsigned char) (value & 0x7F);
				value >>= 7;
				if (value) c |= 0x80;
				m_ostr.write((const char*) &c, 1);
			}
			while (value);
		}

		void CBinaryWriter::writeRaw(const std::string& rawData)
		{
			m_ostr.write(rawData.data(), (std::streamsize) rawData.length());
		}

		void CBinaryWriter::writeRaw(const char* buffer, std::streamsize length)
		{
			m_ostr.write(buffer, length);
		}

		void CBinaryWriter::writeBOM()
		{
			UInt16 value = 0xFEFF;
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			m_ostr.write((const char*) &value, sizeof(value));
		}

		void CBinaryWriter::flush()
		{
			m_ostr.flush();
		}

		bool CBinaryWriter::good()
		{
			return m_ostr.good();
		}

		bool CBinaryWriter::fail()
		{
			return m_ostr.fail();
		}

		bool CBinaryWriter::bad()
		{
			return m_ostr.bad();
		}

		std::ostream& CBinaryWriter::stream() const
		{
			return m_ostr;
		}

		CBinaryWriter::StreamByteOrder CBinaryWriter::byteOrder() const
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			return m_flipBytes ? LITTLE_ENDIAN_BYTE_ORDER : BIG_ENDIAN_BYTE_ORDER;
		#else
			return m_flipBytes ? BIG_ENDIAN_BYTE_ORDER : LITTLE_ENDIAN_BYTE_ORDER;
		#endif
		}

	}//end of namespace stream
}//end of namespace JHCPP